package com.wtf.generator.service.impl;

import cn.hutool.core.collection.ListUtil;
import com.baomidou.dynamic.datasource.DynamicRoutingDataSource;
import com.baomidou.dynamic.datasource.creator.DefaultDataSourceCreator;
import com.baomidou.dynamic.datasource.ds.ItemDataSource;
import com.baomidou.dynamic.datasource.spring.boot.autoconfigure.DataSourceProperty;
import com.baomidou.mybatisplus.extension.toolkit.JdbcUtils;
import com.wtf.generator.common.utils.PageUtils;
import com.wtf.generator.config.DynamicDataSourceConfigProvide;
import com.wtf.generator.constants.DsDbStoreConstant;
import com.wtf.generator.dao.AbstractGeneratorDao;
import com.wtf.generator.enums.CommonErrorEnum;
import com.wtf.generator.exception.CommonException;
import com.wtf.generator.executor.DsDbExecutor;
import com.wtf.generator.model.dto.DsAddDTO;
import com.wtf.generator.model.dto.DsDelDTO;
import com.wtf.generator.model.dto.DsListDTO;
import com.wtf.generator.model.dto.DsPageDTO;
import com.wtf.generator.model.vo.DsVO;
import com.wtf.generator.service.DynamicRoutingDataSourceService;
import com.wtf.generator.support.DsStoreSupport;
import com.zaxxer.hikari.HikariDataSource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import javax.sql.DataSource;
import java.sql.Connection;
import java.util.*;
import java.util.stream.Collectors;
/**
 * date: 2022/6/18 10:59
 * @author linhuanyou
 * @email 496558736@qq.com
 * @version 1.0
 */
@Slf4j
@Service
public class DynamicRoutingDataSourceServiceImpl implements DynamicRoutingDataSourceService {

    @Autowired
    private DynamicDataSourceConfigProvide dynamicDataSourceConfigProvide;

    @Autowired
    private DefaultDataSourceCreator defaultDataSourceCreator;

    @Value("${spring.datasource.dynamic.primary}")
    private String primaryDs;

    @Autowired
    private DsStoreSupport dsStoreSupport;

    @Autowired
    private DsDbExecutor dsDbExecutor;

    @Override
    public List<DsVO> list(DsListDTO dsListDTO) {
     return list(dsListDTO.getDs());
    }

    @Override
    public List<DsVO> list(String ds) {
        return dataSources(ds).stream().map(e->{
            DsVO dsVO = new DsVO();
            dsVO.setPrimary(primaryDs.equals(e.getPoolName()));
            dsVO.setDbType(JdbcUtils.getDbType(e.getJdbcUrl()));
            dsVO.setDs(e.getPoolName());
            Properties dbProperties = null;
            try {
                dbProperties = dsDbExecutor.getDbProperties(e.getJdbcUrl(), dsVO.getDbType());
                dsVO.setHost(dbProperties.getProperty("HOST"));
                dsVO.setPort(Integer.parseInt(dbProperties.getProperty("PORT")));
                dsVO.setDbName(dbProperties.getProperty("DBNAME"));
            } catch (Exception exception) {
                exception.printStackTrace();
            }
            dsVO.setUsername(e.getUsername());
            dsVO.setPassword("******");
            return dsVO;
        }).sorted(Comparator.comparing(DsVO::isPrimary).reversed()).collect(Collectors.toList());
    }

    @Override
    public List<HikariDataSource> dataSources(String ds) {
        List<HikariDataSource> dataSources = new ArrayList<>();
        DynamicRoutingDataSource dynamicRoutingDataSource = dynamicDataSourceConfigProvide.getDynamicRoutingDataSource();
        if(!StringUtils.isEmpty(ds)){
            DataSource dataSource = dynamicRoutingDataSource.getDataSource(ds);
            HikariDataSource hikariDataSource = (HikariDataSource)((ItemDataSource) dataSource).getRealDataSource();
            dataSources.add(hikariDataSource);
        } else {
            Map<String, DataSource> currentDataSources = dynamicRoutingDataSource.getCurrentDataSources();
            for(Map.Entry<String, DataSource> entry : currentDataSources.entrySet()){
                DataSource value = entry.getValue();
                HikariDataSource hikariDataSource = (HikariDataSource)((ItemDataSource) value).getRealDataSource();
                dataSources.add(hikariDataSource);
            }
        }
        return dataSources;
    }


    @Override
    public DsVO add(DsAddDTO dsDbDTO) {
        if(primaryDs.equals(dsDbDTO.getDs())){
            throw new CommonException(CommonErrorEnum.ERROR_PRIMARY_DS);
        }
        DataSourceProperty dataSourceProperty = dsDbExecutor.getDataSourceProperty(dsDbDTO);
        DataSource dataSource = defaultDataSourceCreator.createDataSource(dataSourceProperty);
        if(DsDbStoreConstant.isDsDbStore()){
            int add = dsStoreSupport.addDataSource(dsDbDTO);
        }
        DynamicRoutingDataSource dynamicRoutingDataSource = dynamicDataSourceConfigProvide.getDynamicRoutingDataSource();
        dynamicRoutingDataSource.addDataSource(dataSourceProperty.getPoolName(),dataSource);
        HikariDataSource hikariDataSource = (HikariDataSource)((ItemDataSource) dynamicRoutingDataSource.getDataSource(dataSourceProperty.getPoolName())).getRealDataSource();
        DsVO dsVO = new DsVO();
        BeanUtils.copyProperties(dsDbDTO,dsVO);
        dsVO.setPassword("******");
        return dsVO;
    }

    @Override
    public void remove(DsDelDTO dsDelDTO) {
        if(dsDelDTO.getDs().contains(primaryDs)){
            throw new CommonException(CommonErrorEnum.ERROR_PRIMARY_DS);
        }
        DynamicRoutingDataSource dynamicRoutingDataSource = dynamicDataSourceConfigProvide.getDynamicRoutingDataSource();
        for (String ds : dsDelDTO.getDs()) {
            dynamicRoutingDataSource.removeDataSource(ds);
        }

        if(DsDbStoreConstant.isDsDbStore()){
            int delDataSource = dsStoreSupport.delDataSource(dsDelDTO.getDs());
        }
    }

    @Override
    public PageUtils page(DsPageDTO dsPageDTO) {
        List<DsVO> dataSources = list("");
        if(StringUtils.hasLength(dsPageDTO.getDs())){
            dataSources =  dataSources.stream().filter(e->{return e.getDs().contains(dsPageDTO.getDs());}).collect(Collectors.toList());
        }
        List<DsVO> page = ListUtil.page(dsPageDTO.getPage() - 1, dsPageDTO.getLimit(), dataSources);
        return new PageUtils(page, dataSources.size(), dsPageDTO.getLimit(),dsPageDTO.getPage());
    }

    @Override
    public Boolean testConnect(DsAddDTO dsDbDTO) {
        return dsDbExecutor.testConnectDb(dsDbDTO);
    }
}
